package com.letoken.platform.app.service.impl;

import java.time.LocalDateTime;
import com.letoken.platform.app.service.AppLeTokenService;
import com.letoken.platform.app.service.AppUserWalletService;
import com.letoken.platform.app.util.LeTokenUtil;
import com.letoken.platform.pub.constant.LetokenTokenConstant;
import com.letoken.platform.pub.constant.LetokenUserConstant;
import com.letoken.platform.pub.enums.LetokenApiResponseEnum;
import com.letoken.platform.pub.enums.LetokenTokenTypeEnum;
import com.letoken.platform.pub.enums.TokenApprovedStateEnum;
import com.letoken.platform.pub.enums.TokenTransactionStatusEnum;
import com.letoken.platform.pub.exception.ApiServiceException;
import com.letoken.platform.pub.util.UUIDUtil;
import com.letoken.platform.repo.admin.po.Withdraw;
import com.letoken.platform.repo.app.dao.AppUserMapper;
import com.letoken.platform.repo.app.dao.TokenMapper;
import com.letoken.platform.repo.app.manager.*;
import com.letoken.platform.repo.app.po.*;
import com.letoken.platform.repo.app.redis.AppWalletBlackListRedis;
import com.letoken.platform.repo.app.redis.AppWalletRateLimitRedis;
import com.letoken.platform.repo.app.req.token.WithdrawLetReq;
import com.letoken.platform.repo.utlis.GasUtils;
import com.letoken.platform.repo.utlis.LocalBlockUtils;
import com.letoken.platform.repo.utlis.UserLockedTool;
import com.letoken.platform.repo.web3.util.EvmUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * @author wyl
 * @date 2022/12/22 11:24
 * @Version 1.0
 */
@Service
@Slf4j
public class AppLeTokenServiceImpl implements AppLeTokenService {

//    @Resource
//    private AppUserAddressManager appUserAddressManager;
    @Resource
    private AppUserWalletManager walletManager;
    @Autowired
    private AppUserManager appUserManager;
    @Resource
    private AppUserWalletService appUserWalletService;
    @Resource
    private TokenMapper tokenMapper;
    @Autowired
    AppUserMapper appUserMapper;
    @Resource
    private AppUserLockedManager appUserLockedManager;

    @Resource
    private GasUtils gasUtils;



    @Transactional(rollbackFor = Exception.class)
    @Override
    public void tokenWithdraw(WithdrawLetReq req) {
        String leUserAddressOrUserId = req.getUserAddressOrUserId();
        if (!StringUtils.startsWithIgnoreCase(leUserAddressOrUserId, LetokenUserConstant.USER_ID_PREFIX)) {
            //验证地址有效性
            if (!EvmUtil.checkAddressChecksum(leUserAddressOrUserId)) {
                throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_WITHDRAW_015);
            }
        }
        String userAddressOrUserId = LeTokenUtil.appUserIdStartsWithLe(leUserAddressOrUserId);
        BigDecimal amount = req.getAmount();
        if (amount.scale() > LetokenTokenConstant.AMOUNT_SCALE){
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_WITHDRAW_020);
        }

        AppUser user = appUserManager.queryMe();
        Integer userId = user.getId();

        // 黑名单
        if(AppWalletBlackListRedis.me().getValue(userId.toString())){
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_WITHDRAW_017);
        }

        // 频率限制
        if(AppWalletRateLimitRedis.me().isLimited(userId.toString(),"withdraw", 1)) {
            // 加入黑名单
            AppWalletBlackListRedis.me().setValue(userId.toString(), true);
            // 冻结用户

            UserLockedTool.submit(()->{
                appUserLockedManager.setUserLocked(userId, true);
            });

            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_WITHDRAW_017);
        }

        // 判断冻结用户
        if(appUserLockedManager.isLocked(userId)){
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_WITHDRAW_017);
        }
        BigDecimal fee = gasUtils.getFee(BigDecimal.ZERO,1);
        gasUtils.systemGasFee(userId,fee);
        Integer tokenId = LetokenTokenTypeEnum.LET.getId();
        Token token = tokenMapper.selectById(tokenId);
        // todo 去掉AppUserAddress表
//        AppUserAddress userAddress = appUserAddressManager.queryWalletAddressByAddressOrId(userAddressOrUserId);
//        //转账用户不存在
//        if (userAddress == null){
//            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_WITHDRAW_004);
//        }
//        if (userAddress.getUserId() == null){
//            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_WITHDRAW_004);
//        }
//        // 不能给自己提现
//        if (userAddress.getUserId().equals(userId)){
//            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_WITHDRAW_003);
//        }
        LocalBlockUtils.AddressPo  po = new LocalBlockUtils.AddressPo();
        if (userAddressOrUserId.startsWith("0x")){
            po = LocalBlockUtils.obtainUserId(LocalBlockUtils.ObtainUserIdDto.builder()
                    .addr(userAddressOrUserId)
                    .coinId(tokenId)
                    .build());
        }else {
            Integer toId = null;
            try {
                toId = Integer.valueOf(userAddressOrUserId);
            } catch (NumberFormatException e) {
                log.error("用户地址转换失败!{}",userAddressOrUserId,e);
                throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_WITHDRAW_004);
            }
            if (null == appUserMapper.userExists(toId)){
                log.error("用户提现错误-用户不存在:{}", toId);
                throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_WITHDRAW_004);
            }

            // todo 通过用户ID转账不关心地址
            po.setUserId(toId);
            po.setSuccess(true);
        }
        if (!po.isSuccess()){
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_WITHDRAW_004);
        }
//        if (po.getUserId() == null){
//            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_WITHDRAW_004);
//        }
        // 不能给自己提现
        if (po.getUserId().equals(userId)){
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_WITHDRAW_003);
        }




        //平台内互转金额要大于0
        if (amount.compareTo(BigDecimal.ZERO) <= 0){
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_WITHDRAW_012);
        }
        AppUserWallet fromAppUserWallet = walletManager.queryWalletByToken(userId, tokenId);
        //提现金额是否大于自己的金额 大于就报错
        if (amount.compareTo(fromAppUserWallet.getAvailableBalance()) > 0){
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_WITHDRAW_007);
        }
        String uuid = UUIDUtil.getUUID(16);
        LocalDateTime now = LocalDateTime.now();
        Withdraw withdraw = new Withdraw();
        withdraw.setSn(uuid);
        withdraw.setUserId(userId);
        withdraw.setTokenId(tokenId);
        withdraw.setChainId(token.getChainId());
        //收款地址
        withdraw.setAddress(leUserAddressOrUserId);
        withdraw.setToId(po.getUserId());
        withdraw.setAmount(amount);
        withdraw.setFeeAmount(BigDecimal.ZERO);
        // 平台互转 transfer_mode 为0 反之为1
        withdraw.setTransferMode(0);
        withdraw.setTxid("");
        withdraw.setStatus(TokenTransactionStatusEnum.PENDING.getValue());
        withdraw.setIsAuto(true);
        withdraw.setCreateTime(now);
        withdraw.setFeeAmount(fee);
        withdraw.setResponse("");
        //自动审核
        withdraw.setApprovedBy(LetokenTokenConstant.SYSTEM_AUTO);
        withdraw.setRemark("");

        //后续添加 开始
        withdraw.setRealAmount(withdraw.getAmount());
        withdraw.setGasUsed(BigDecimal.ZERO);
        // 结束
        withdraw.setApprovedState(TokenApprovedStateEnum.ADOPT.getValue());
        boolean insert = withdraw.insert();
        if (insert){
//            WithdrawReq withdrawReq = new WithdrawReq();
//            withdrawReq.setId(withdraw.getId().toString());
//            walletMQManager.withdraw(rabbitTemplate,withdrawReq,1000);
            appUserWalletService.updateWithdrawInternalOrder(withdraw, TokenTransactionStatusEnum.COMPLETED);
        }else {
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_WITHDRAW_008);
        }
    }
}
